简单、方便、快速开发嵌入式实时系统——用MicroPython就对了
嵌入式实时系统正变得越来越复杂,不仅需要你深入了解微控制器本身,还需要你了解各种传感器、算法、互联网协议以及各种终端应用。随着产品的更新迭代加快,更短的开发周期和更多的功能这两者看似矛盾的对立面却成了产品升级换代最关键的因素,工程师需要找到既能够加速产品设计的方法,同时最好又能够将原本的代码移植到新的产品中,省事实力——这就需要一个集成强大、丰富功能且灵活的开发平台。
市面上也有不少厂商推出一系列的专用微控制器用于加速开发过程,但这些解决方案基本是将工程师与所使用的微控制器厂商绑在了一块,如果需要将代码从一个平台移植到另一个平台,那可能会耗费大量时间和成本,得不偿失。但随着MicroPython的出现,一切都变得与众不同了,这是目前业界可以接受并广泛采用的解决方案之一。MicroPython可以运行在不同厂商、不同微控制器产品上,并且还是开源的,这让工程师可以随时根据自己的需求使用和定制,摆脱单一厂商、单一微控制器的束缚,极其灵活方便。
MicroPython是基于Python3的精简且高效的编程语言,其中包括一小部分优化过、可以在微控制器和受限环境中运行的Python标准库。MicroPython一开始是kick-starter上的一个众筹项目,如今不仅获得资助,而且成长良好。截止到目前,已经有不少嵌入式硬件成功移植了Micropython,如pyboard、esp8266、WiPy、Espruino Pico、STM32F4 Discovery等,并且已经成功应用于多个行业项目,如工业、物联网等。
如何选择运行MicroPython的微控制器
MicroPython可以运行在多种不同的微控制器上,如果一款微控制器具备足够的RAM、闪存和性能来运行解释器,那么MicroPython几乎可以移植到任何一款微控制器上。所以,如果要选择运行MicroPython的微控制器,工程师应该要注意以下几个关键点:
至少256KB的闪存
至少16KB的RAM
至少有一个80 MHz的CPU时钟
以上是通常情况下的建议,但这是可变化的,工程师可以根据他们的应用需求以及他们到底想要花费多少时间来定制MicroPython内核来使上面的条件发生变化,比如工程师可以将MicroPython修改到能运行在远远小于256 KB闪存的微控制器上。以上的建议主要是为工程师提供最佳体验,并为他们的应用程序代码提供更新、增加新功能等操作的“成长”空间。
MicroPython已经有不少移植到不同微控制器上的案例,你可以从这些案例中开始学习,更进一步的话,你可以根据现有的案例举一反三,将MicroPython移植到新的平台上。下图则是MicroPython的源代码主目录结构。
可以看到现在MicroPython支持的几种不同的微控制器产品:
ARM系列微控制器
德州仪器的 CC3200
Adafruit的 ESP8266
Microchip的16位PIC单片机
意法半导体的STM32系列微控制器
根目录中列出的每个文件夹都很重要,其中包含了该微控制器系列的一般驱动程序和支持文件,每个文件夹下都可能有几个不同的开发板或处理器。例如,stmhal文件夹支ST的STM32F429 Discovery Board、STM32 IoT Discovery Node (STM32L)以及Adafruit的STM32F405 pyboard等产品;而ESP8266文件夹包含了一些对ESP8266 WiFi模块支持的板子。
能运行MicroPython的开发板价格很多都很便宜,工程师完全可以购买多个开发板来反复验证项目中采用MicroPython的可行性,同时也能了解实际项目的应用程序到底需要多大的RAM,Flash以及处理能力的需求。
另外,值得一提的是工程师编写的MicroPython应用程序代码不一定必须存储在微控制器的内部Flash中,虽然MicroPython内核需要位于微控制器的Flash上,但应用程序代码可以放在外部存储上,比如microSD。使用外部存储可以有效降低对微控制器内部Flash的需求,一定程度上大大降低了整个系统成本。
启动/运行MicroPython
笔者之前使用过原版的基于STM32F405微控制器的pyboard(pyboard评测),这个版本的平台购买时已经预装了MicroPython,所以可以直接拿来使用。但是,其它开发套件或定制硬件都需要工程师自己下载MicroPython源代码,然后为目标开发板构建源代码,最后再下载MciroPython到微控制器中。
MicroPython的源代码全部在Github上,获取非常容易,工程师也只需要简单设置几个工具链、配置下环境就可以构建属于自己开发板的MicroPython。下面讲下具体流程,以ST的STM32F429探索板为例。
首先,工程师需要一个运行Linux系统的电脑,当然也可以通过虚拟机实现,然后通过以下命令安装ARM编译器工具链:
sudo apt-get install gcc-arm-none-eabi
如果是新装的Linux系统,可能没有Git,那可以使用以下命令安装Git:
sudo apt-get install git
安装git之后,就可以通过在终端中执行以下命令获取MicroPython的源代码:
git clone https://github.com/micropython/micropython.git
该过程可能会执行几分钟,工程师可以看到相应的下载安装信息。
一旦MicroPython源代码被克隆到本地文件系统中,找到该文件目录,在终端中执行“cd stmhal”。
stmhal目录包含了用于STM32微控制器的MicroPython的makefile。另外,工程师也可以通过查看“boards”文件夹下的内容了解当前MicroPython支持的所有STM32开发板信息,根据这些信息,工程师就可以构建存在于“开发板”文件夹中的任何开发板。
例如,工程师可以通过键入以下命令来构建STM32F4探索板:
make BOARD=STM32F4DISC
MicroPython将需要几分钟的时间来构建,在构建过程中,我们也别闲着,可以安装设备固件更新工具DFU,这个工具可以通过USB将MicroPython编程到微控制器上,安装命令为:
sudo apt-get install dfu-util
当MicroPython构建完成并安装了dfu-util后,我们就可以将MicroPython下载到微控制器上。首先将微控制器置于DFU引导加载程序模式(将启动引脚设置为在复位时加载内部引导加载程序)。
当微控制器处于引导加载程序模式并通过USB连接到主机时,可以dfu-util使用以下命令下载:
dfu-util -a 0 -d 0483:df11 -D build-STM32F4DISC / firmware.dfu
dfu-util将下载编译过程输出的dfu文件,这个过程需要几分钟时间,因为微控制器将被完全擦除并重新编程,具体过程类似下图。下载完成后,需要将启动引脚重新设置为从内部闪存加载,然后重新启动微控制器即可。
构建复杂的实时系统:连接传感器和通信器件
使用MicroPython等高级编程语言开发实时嵌入式软件的最大优点是该软件与底层硬件无关,这意味着工程师可以开发出MicroPython脚本在pyboard上运行,而且在几乎不需要多大修改的前提下,该脚本还可运行在ESP8266或STM32F4探索板上。
下面就让我们来看一下基本的MicroPython脚本如何将博世Sensortec BMP280气压计&温度传感器连接到I2C总线,然后使用Microchip RN-42蓝牙模块通过蓝牙串行链路传输数据。BMP280是一个I2C接口的气压计&温度计,首先需要将它通过I2C接口连接到pyboard上,市面上有很多的转接板可以使用,比如DFRobot的Gravity board转接板。一旦硬件组合完成,那使用MicroPython脚本就变得极为简单。
首先,工程师将从pyb库导入I2C,这里的PYB库提供了接入到微控制器外围功能,如SPI,I2C以及UART。在使用任何外设之前,工程师必须实例化外设类以创建可用于控制外设的对象。一旦外设类被初始化,工程师可以执行任何其他初始化,例如在进入主应用程序循环之前验证设备是否存在,主要应用程序代码将会每秒采样一次传感器。示例代码如下所示:
from pyb import I2C
GlobalTemp = 0.0
GlobalBarometer = 0.0
#初始化和实例化I2C外设2
I2C2 = I2C(2,I2C.MASTER,波特率= 100000)
while True:
SensorSample()
pyb.delay(1000)
def SensorSample():
#读取温度数据
TempSample = I2C2.readfrom_mem(119,0xFA,3)
#读取压力数据
PressureSample = I2C2.readfrom_mem(119,0xF7,3)
此代码为MicroPython脚本,用于初始化I2C外设并与DFRobot重力板通信以获取温度和气压计传感器数据。
对传感器数据进行采样而并不做其它操作的话是没有任何意义的,换句话说,根本体现不出MicroPython的强大,许多工程师面临的技术挑战是将传感器设备连接到互联网或使用蓝牙连接到本地传感器集线器上。
使用RN-42添加蓝牙连接,可以将RN-42与微控制器通过UART数据的模式通信,然后通过蓝牙传输数据,并且让RN-42处理整个蓝牙堆栈,如下图所示。
一旦连接了蓝牙,工程师就可以创建一个非常简单的脚本,将接收到的传感器数据传输到移动设备,进一步的话,可以保存数据或将数据转发到云端以供进一步分析。例如:
from pyb import uart
from pyb import I2C
GlobalTemp = 0.0
GlobalBarometer = 0.0
#初始化和实例化I2C外设2
I2C2 = I2C(2,I2C.MASTER,波特率= 100000)
#配置Uart1进行通信
Uart1 = pyb.UART(1,115200)
Uart1.init(115200,bits = 8,parity = None,stop = 1)
while True:
SampleSensor()
pyb.delay(1000)
def SensorSample():
#读取温度数据
TempSample = I2C2.readfrom_mem(119,0xFA,3)
#读取压力数据
PressureSample = I2C2.readfrom_mem(119,0xF7,3)
# 将样本数据转换为字符串
data =“#,temperature =”str(TempSample)+“,pressure”+ str(PressureSample)+“,#,\ n \ r”
#将数据写入蓝牙
Uart1.write(data)
示例代码显示了MicroPython脚本初始化UART1并与外部设备通信。
不仅Python应用程序代码可以轻松移植到其它硬件平台,应用程序中的通用库和功能也可以帮助工程师加速开发。创建上述应用程序可以在一个小时或更短的时间内完成,不过对于刚入门的工程师来说,可能需要一周或更久的时间才能完成。
开发实时应用程序的技巧和窍门
使用MicroPython开发嵌入式应用程序非常简单,但从系统获取实时性能可能并不像人们想象的那样简单。虽然MicroPython为简化和重用代码提供了巨大的优势,但如果工程师不知道一些有趣的东西和库,那么从实时系统获取可预测和一致的时机可能会具有挑战性。
MicroPython包含一个垃圾收集器,它在后台运行并管理堆和其他内存资源。垃圾收集器是非确定性的,因此如果垃圾收集器在时间非常严格的点开始执行,那么期望确定性行为的工程师可能会陷入麻烦。这里有几条建议,工程师应该遵循,以确保不会发生这种情况。
首先,工程师可以导入垃圾收集库gc,并使用enable和disable方法来控制何时启用或禁用垃圾收集器。工程师可以在时间关键点之前禁用垃圾收集,然后如图所示启用它。
import gc
gc.disable()
#My time critical code
gc.enable()
在时间关键代码部分之前禁用MicroPython垃圾回收器。
其次,工程师也可以手动控制垃圾收集过程。当工程师创建和销毁对象时,他们会在堆上分配内存,垃圾收集器运行并释放未使用的空间。由于它是以不规则的间隔执行的,因此工程师可以使用collect方法定期运行垃圾回收以确保堆空间不会被垃圾填满。完成此操作后,垃圾收集运行可以从10毫秒降低到每次运行少于1毫秒。手动调用垃圾回收还可确保工程师的应用程序可以控制非确定性定时代码。这使他们可以决定何时运行垃圾回收并确保其应用程序具有实时性能。
工程师如果对编写实时代码感兴趣的话,这里有几种非常好的实践方式,包括:
为通信通道使用预分配的缓冲区
使用通信外设时采用readinto方法
避免传统的Python文档使用###
在运行时最大限度地减少对象的创建和破坏
监视应用程序执行时间
有兴趣详细了解以上“实践方式”的工程师可以点击链接http://docs.micropython.org/en/latest/pyboard/reference/speed_python.html 查看MicroPython的推荐文档。
结论
对于希望实现与底层微控制器硬件无关的实时嵌入式应用程序的工程师,MicroPython是一个有趣、易用的平台。工程师可以使用MicroPython中提供的标准库编写他们的高级Python脚本,并在“任意的”微控制器上运行,这为工程师提供了众多的优势,包括:
提高应用程序的多重利用
加快上市时间
将应用程序与硬件分离
MicroPython并不适用于所有应用,但迄今为止在工业和物联网应用方面取得了不小成功,同时还能实现快速原型设计和概念验证,总之,想要简单、方便、快速开发嵌入式实时系统,用MicroPython就对了。
阅读推荐